#include <cassert>
#include <iostream>
#include <functional>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>

#include "TCPServer.h"

TCPServer::TCPServer(const char *ip, uint16_t port, size_t cores)
        : socket_fd(-1), IP(ip), port(port), thread_pool(std::make_unique<ThreadPool>(cores)),
          timer(std::make_unique<Timer>()),
          epoller(std::make_unique<Epoller>(1024)) {
    this->init_socket();
}

int TCPServer::set_fd_non_block(int fd) {
    return fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK);
}

void TCPServer::read_connection(HTTPConnection *connection, int client_fd) {
    int error = 0;
    if (connection->read(error)) {
        /** TODO: 处理读事件后该做什么
         * - 判断是否是长连接吗？（keep-alive）
         * - 是的话保持连接，加入epoll事件组，不是的话关闭该连接
         */
        connection->parse_connection();
        this->epoller->modify_event(connection->get_client_fd(),
                                    EPOLLOUT | EPOLLET | EPOLLONESHOT | EPOLLRDHUP);  // 注册写事件
        return;
    }

    disconnection(client_fd);
}

void TCPServer::write_connection(HTTPConnection *connection, int client_fd) {
    int error = 0;

    if (connection->write(error)) {
        /** TODO: 处理写事件后该做什么
        * - 判断是否是长连接吗？（keep-alive）
        * - 是的话保持连接，加入epoll事件组，不是的话关闭该连接
        */
        if (error == EAGAIN) {
            this->epoller->modify_event(connection->get_client_fd(),
                                        EPOLLOUT | EPOLLET | EPOLLONESHOT | EPOLLRDHUP);  // 注册写事件
            return;
        } else if (connection->get_is_keep_alive()) {
            this->epoller->modify_event(connection->get_client_fd(),
                                        EPOLLIN | EPOLLET | EPOLLONESHOT | EPOLLRDHUP);  // 注册读事件
            return;
        }

        disconnection(client_fd);
    }
}

void TCPServer::disconnection(int client_fd) {
    this->epoller->delete_event(client_fd);  // 删除事件
    this->connections.erase(client_fd);  // 删除连接
}

bool TCPServer::init_socket() {
    int result = 0;

    this->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_fd == -1) {  // TODO: 可以考虑封装一下错误处理函数；有些错误不应该直接退出程序
        std::cerr << "ERROR: Failed to create Socket.\n"; // TODO: 此处应该调用日志输出错误
        return false;
    }

    struct sockaddr_in server_address{};
    socklen_t server_length = sizeof(server_address);
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr(this->IP);
    server_address.sin_port = htons(this->port);

    // 延迟关闭TCP连接
    struct linger linger_option{1, 1};
    result = setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, &linger_option, sizeof(linger_option));
    if (result == -1) {
        close(socket_fd);
        std::cerr << "ERROR: Failed to set Socket option: LINGER.\n"; // TODO: 此处应该调用日志输出错误
        return false;
    }

    // 端口复用
    int option_value = 1;
    result = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_value, sizeof(option_value));
    if (result == -1) {
        close(socket_fd);
        std::cerr << "ERROR: Failed to set Socket option: REUSEADDR.\n"; // TODO: 此处应该调用日志输出错误
        return false;
    }

    result = bind(socket_fd, (struct sockaddr *) &server_address, server_length);
    if (result == -1) {
        close(socket_fd);
        std::cerr << "ERROR: Failed to bind socket to port " << port << ".\n";
        return false;
    }

    result = listen(socket_fd, SOMAXCONN);
    if (result < 0) {
        close(socket_fd);
        std::cerr << "ERROR: Failed to listen socket.\n";
        return false;
    }

    // 将socket_fd放入epoll事件处理中
    this->epoller->add_event(socket_fd, EPOLLIN | EPOLLET | EPOLLRDHUP);
    set_fd_non_block(this->socket_fd);
    return true;
}

void TCPServer::start() {
    std::cout << "Server started on " << this->IP << ":" << this->port << std::endl;
    // 单独一个线程处理定时器方法
    thread_pool->enqueue([capture0 = timer.get()] { capture0->clean_inactive_connection(); });
    while (true) {
        int events = this->epoller->epoller_wait(-1);
        for (int i = 0; i < events; ++i) {
            int fd = this->epoller->get_event_fd(i);
            uint32_t event = this->epoller->get_events(i);

            if (fd == this->socket_fd) {
                struct sockaddr_in client_address{};
                socklen_t client_length = sizeof(client_address);
                int client_fd = accept(this->socket_fd, (struct sockaddr *) &client_address, &client_length);
                if (client_fd < 0) {
                    std::cerr << "ERROR: Failed to accept socket.\n";
                    break;
                }
                this->connections[client_fd].init_connection(client_fd, client_address);
                // 设置定时器
                timer->add(client_fd, std::chrono::milliseconds(60000),
                           [this, client_fd] { disconnection(client_fd); });
                epoller->add_event(client_fd, EPOLLIN | EPOLLET | EPOLLONESHOT | EPOLLRDHUP);
                set_fd_non_block(client_fd);

//                } else if (HTTPConnection::connections_count >= 65536) {
//                    std::cerr << "Connections is full, Server is busy now.\n";
//                    continue;
//                }

            } else if (event & EPOLLIN) {
                assert(connections.count(fd) > 0);
                // 定时器检测连接时长
                timer->update(fd, std::chrono::milliseconds(60000));
                thread_pool->enqueue(&TCPServer::read_connection, this, &connections[fd], fd);
            } else if (event & EPOLLOUT) {
                assert(connections.count(fd) > 0);
                thread_pool->enqueue(&TCPServer::write_connection, this, &connections[fd], fd);
            } else if (event & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                assert(connections.count(fd) > 0);
                // TODO: 处理客户端关闭连接
                disconnection(fd);
            } else {
                // TODO: 处理以上情况之外事件

            }
        }
    }
}